﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Diagnostics;
using System.Threading;
//using System.Windows;
using System.Windows.Forms;
using System.Xml;

namespace SQLServerSyncServer
{
    public partial class Form1 : Form
    {
        DBUtility db;
        DBUtility db2;
        UDP udp; // 发送类

        private DateTime begin; // 时段的开始和结束
        private int currentId;  // 当前数据的主键id
        private int endId;
        private int interval; // 发包间隔

        Thread sendThread;
        Thread lastDaySendThread;

        Table table;    // 存放xml中的表信息

        public Form1()
        {
            InitializeComponent();
        }

        private void listView1_SizeChanged(object sender, EventArgs e)
        {
            this.columnHeader1.Width = listView1.Width / 6;
            this.columnHeader2.Width = listView1.Width / 6;
            this.columnHeader5.Width = listView1.Width / 2;
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            table = new Table();
            loadConfig();
            loadXML();
            tabControl1.SelectTab(this.tabPage2);

            begin = dateTimePicker1.Value;
            interval = (int)numericUpDown1.Value;
        }

        private bool VerificationConfig()
        {
            bool ret = true;
            if (this.textBox1.Text.Trim() == "")
            {
                Log.Message(ref listView1, DateTime.Now, "SQLServer服务器IP必须填写");
                ret = false;
            };
            if (this.textBox2.Text.Trim() == "")
            {
                Log.Message(ref listView1, DateTime.Now, "数据库用户名必须填写");
                ret = false;
            };
            if (this.textBox4.Text.Trim() == "")
            {
                Log.Message(ref listView1, DateTime.Now, "数据库名称必须填写");
                ret = false;
            };

            Log.Message(ref listView1, DateTime.Now, "SQLServer服务器配置OK！");
            return ret;
        }


        #region 消息推送主线程方法
        private void messageSend()
        {
            bool ifGetID = false;
            int queryCountNum = 1000;
            while (true)
            {
                try
                {
                    if (!ifGetID)
                    {
                        bool ret = getMinIdByTime();
                        if (ret)
                        {
                            ifGetID = true;
                            continue;
                        }
                        if (!ret || currentId == 0)
                        {
                            threadLogMsg(DateTime.Now, "或者停止服务后重设时间");
                            threadLogMsg(DateTime.Now, "15分钟后自动重试");
                            threadLogMsg(DateTime.Now, "从时间[" + begin.ToString() + "]开始未找到数据");

                            Thread.Sleep(1000 * 60 * 15);
                            continue;
                        }
                    }

                    int queryCnt = 0;
                    DataTable data = getDB(queryCnt, queryCountNum);

                    int arrCnt = data.Rows.Count;

                    if (arrCnt > 0)
                    {
                        while (queryCnt <= 2)   // 三次冗余重新取数据，每次取1000条(id范围，但是最终取的数量已查到的数据最大值为准)，然后推送取到的数据
                        {
                            if (queryCnt > 0)
                            {
                                data = getDB(queryCnt, queryCountNum);
                            }

                            threadLogMsg(DateTime.Now, "本次ID范围[" + currentId + " - " + endId + "], 第" + (queryCnt + 1) + "轮取数");
                            threadLogMsg(DateTime.Now, "第" + (queryCnt + 1) + "轮取数,获取" + data.Rows.Count + "条数据推送中.....");
                            Console.WriteLine("本次ID范围[" + currentId + " - " + endId + "], 第" + (queryCnt + 1) + "轮取数");

                            dataSend(data);
                            Thread.Sleep(interval * 2); 
                            queryCnt++;
                        }

                        updateStatus(data);
                    }
                    else
                    {
                        threadLogMsg(DateTime.Now, "15分钟后自动重试");
                        threadLogMsg(DateTime.Now, "从ID " + currentId.ToString() + " 后未采集到数据");
                        Thread.Sleep(1000 * 60 * 15);
                    }
                }
                catch (ThreadAbortException) { }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.ToString());
                    threadLogMsg(DateTime.Now, ex.Message);
                }
            }
        }

        //
        // 摘要:
        //     查到的数据，每次推送5条
        //
        private void dataSend(DataTable data)
        {
            int dataCount = data.Rows.Count;
            if(dataCount <= 0) return;

            DataTable newDt = data.Clone();
            int numCnt = 4;
            for (int idx = 0; idx < dataCount; idx++)
            {
                newDt.ImportRow(data.Rows[idx]);
                if (idx == numCnt || idx == dataCount - 1)
                {
                    byte[] objBytes = ObjectTransfer.ObjectToBytes(newDt);

                    udp.sendBytes(objBytes, textBox6.Text.Trim(), (int)this.numericUpDown3.Value);

                    newDt.Clear();
                    numCnt += 5;
                    Thread.Sleep(interval);
                }
            }
        }

        private void updateStatus(DataTable data)
        {
            currentId = int.Parse(data.Compute("Max(OriginalDataID)", "").ToString());
            Console.WriteLine(currentId.ToString());

            DataRow[] maxRow = data.Select("OriginalDataID=" + currentId);

            if (maxRow != null && maxRow.Length > 0)
            {
                begin = DateTime.Parse(maxRow[0]["mTime"].ToString());
            }
            Console.WriteLine(begin.ToString());

        }

        private DataTable getDB(int queryCnt, int queryNum)
        {
            try
            {
                string sql = string.Empty;
                if (queryCnt == 0)
                {
                    sql = "SELECT top " + queryNum + " * FROM " + table.name + " WHERE " + table.UID + " > " + currentId + " order by " + table.UID;
                }
                else
                {
                    sql = "SELECT * FROM " + table.name + " WHERE " + table.UID + " > " + currentId + "  AND  " + table.UID + " <= " + endId;
                }

                DataSet ds = db.ExecuteDS(sql);
                if (ds == null || ds.Tables.Count <= 0 || ds.Tables[0].Rows.Count <= 0)
                {
                    return new DataTable();
                }

                endId = int.Parse(ds.Tables[0].Compute("Max(OriginalDataID)", "").ToString());
                return ds.Tables[0];
            }
            catch (Exception exc)
            {
                threadLogMsg(DateTime.Now, exc.ToString(), table.name);
                Console.WriteLine(exc.ToString());
                return new DataTable();
            }
        }

        private bool getMinIdByTime()
        {
            string sql = "SELECT MIN(OriginalDataID) FROM " + table.name + " WHERE " + table.time + " >= '" + begin.ToString() + "'";
            object ret = db.ExecuteScalar(sql);
            if (ret != null && !"".Equals(ret.ToString()))
            {
                currentId = int.Parse(ret.ToString()) - 1;
                return true;
            }
            else
            {
                return false;
            }
        }
        #endregion



        #region 补充消息推送线程方法1
        private void lastDayMessageSend()
        {
            int queryCountNum = 1000;
            
            int lastDayDataId = -1;

            while (true)
            {
                try
                {
                    if (lastDayDataId == -1)
                    {
                        lastDayDataId = getLastDayMinIdByTime();
                        if (lastDayDataId > 0)
                        {
                            continue;
                        }
                        if (lastDayDataId == -1)
                        {
                            Thread.Sleep(1000 * 60 * 15);
                            continue;
                        }
                    }

                    DataTable data = getLastDayDB(queryCountNum, lastDayDataId);

                    if (data != null && data.Rows.Count > 0)
                    {
                        int lastDayDataEndId = int.Parse(data.Compute("Max(OriginalDataID)", "").ToString());
                        Console.WriteLine("补推线程 [" + lastDayDataId + "至" + lastDayDataEndId + "], 共" + data.Rows.Count);
                        Console.WriteLine("补推线程 [" + data.Compute("Max(mTime)", "").ToString() + "]");
                        dataSend(data);


                        Console.WriteLine("补推线程 [" + lastDayDataId + "至" + lastDayDataEndId + "], 推送完成共" + data.Rows.Count);
                        lastDayDataId = lastDayDataEndId;
                        Thread.Sleep(interval);
                    }
                    else
                    {
                        Console.WriteLine("补推线程 没得补啦，四小时后再来一次");

                        lastDayDataId = -1;
                        Thread.Sleep(1000 * 60 * 60 * 4);
                    }
                }
                catch (ThreadAbortException) { }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.ToString());
                }
            }
        }
      

        private DataTable getLastDayDB(int queryNum, int lastDayDataId)
        {
            try
            {
                //string nowDayString = DateTime.Now.ToString("yyyy-MM-dd") + " 00:00:00";
                string nowDayString = begin.ToString("yyyy-MM-dd") + " 00:00:00";
                string sql = "SELECT top " + queryNum + " * FROM " + table.name + " WHERE " + table.UID + " > " + lastDayDataId + " AND " + table.time + " < '"+ nowDayString + "' order by " + table.UID;
                Console.WriteLine(sql);

                DataSet ds = db2.ExecuteDS(sql);
                if (ds == null || ds.Tables.Count <= 0 || ds.Tables[0].Rows.Count <= 0)
                {
                    return null;
                }

                return ds.Tables[0];
            }
            catch (Exception exc)
            {
                Console.WriteLine(exc.ToString());
                return null;
            }
        }

        private int getLastDayMinIdByTime()
        {
            //string dateCondition = DateTime.Now.AddDays(-2).ToString("yyyy-MM-dd") + " 00:00:00";
            string dateCondition = begin.AddDays(-2).ToString("yyyy-MM-dd") + " 00:00:00";
            string sql = "SELECT MIN(OriginalDataID) FROM " + table.name + " WHERE " + table.time + " >= '" + dateCondition + "'";
            object ret = db2.ExecuteScalar(sql);
            if (ret != null && !"".Equals(ret.ToString()))
            {
                return int.Parse(ret.ToString()) - 1;
            }
            else
            {
                return -1;
            }
        }
        #endregion 


        private void button1_Click(object sender, EventArgs e)
        {
            saveConfig();
            Log.Message(ref listView1, DateTime.Now, "设置保存成功！");
            MessageBox.Show("设置保存成功");
        }

        private void button2_Click(object sender, EventArgs e)
        {
            string server = this.textBox1.Text.Trim();
            string dbname = this.textBox4.Text.Trim();
            string uid = this.textBox2.Text.Trim();
            string password = this.textBox3.Text.Trim();
            try
            {
                db = new DBUtility(server, dbname, uid, password);
                db2 = new DBUtility(server, dbname, uid, password);
                udp = new UDP(textBox7.Text.Trim(), (int)this.numericUpDown2.Value);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
                MessageBox.Show(ex.Message);
                return;
            }
            tabControl1.SelectTab(this.tabPage1);
            if (!VerificationConfig()) tabControl1.SelectTab(this.tabPage2);

            sendThread = new Thread(new ThreadStart(messageSend));
            lastDaySendThread = new Thread(new ThreadStart(lastDayMessageSend));
            sendThread.Start();
            lastDaySendThread.Start();

            Log.Message(ref listView1, DateTime.Now, "发送服务启动！");
            this.button2.Enabled = false;
        }


        private void button3_Click(object sender, EventArgs e)
        {
            if (sendThread != null)
                sendThread.Abort();
           
            tabControl1.SelectTab(this.tabPage1);

            Log.Message(ref listView1, DateTime.Now, "发送服务停止！");
            WriteConfig("begin", begin.ToString());

            this.button2.Enabled = true;
            if (udp != null)
                udp.Close();
        }

        private void button5_Click(object sender, EventArgs e)
        {
            if (sendThread != null)
                sendThread.Abort();

            tabControl1.SelectTab(this.tabPage1);

            Log.Message(ref listView1, DateTime.Now, "发送服务停止！");
            WriteConfig("begin", begin.ToString());

            this.button2.Enabled = true;
            if (udp != null)
                udp.Close();
        }

        private void dateTimePicker1_ValueChanged(object sender, EventArgs e)
        {
            begin = dateTimePicker1.Value;
        }

        private string ReadConfig(string name)
        {
            Configuration config = System.Configuration.ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
            KeyValueConfigurationElement key = config.AppSettings.Settings[name];
            if (key == null) return "";
            else
                return key.Value;
        }

        private void WriteConfig(string name, string value)
        {
            Configuration config = System.Configuration.ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
            config.AppSettings.Settings.Remove(name);
            config.AppSettings.Settings.Add(name, value);
            //cfa.AppSettings.Settings[name].Value = value;
            config.Save();
        }


        private void Form1_FormClosed(object sender, FormClosedEventArgs e)
        {
            if (sendThread != null)
                sendThread.Abort();
            if (lastDaySendThread != null)
                lastDaySendThread.Abort();
            saveConfig();

            if (udp != null)
                udp.Close();
        }

        void loadConfig()
        {
            string tmp = ReadConfig("dbServer");
            if (tmp != "") textBox1.Text = tmp;

            tmp = ReadConfig("Uid");
            if (tmp != "") textBox2.Text = tmp;
            tmp = ReadConfig("Password");
            if (tmp != "") textBox3.Text = tmp;
            tmp = ReadConfig("dbName");
            if (tmp != "") textBox4.Text = tmp;

            tmp = ReadConfig("Client");
            if (tmp != "") textBox6.Text = tmp;
            tmp = ReadConfig("Server");
            if (tmp != "") textBox7.Text = tmp;

            tmp = ReadConfig("SendPort");
            if (tmp != "") numericUpDown2.Value = int.Parse(tmp);
            tmp = ReadConfig("RecvPort");
            if (tmp != "") numericUpDown3.Value = int.Parse(tmp);
            tmp = ReadConfig("begin");
            if (tmp != "") dateTimePicker1.Value = DateTime.Parse(tmp);
            tmp = ReadConfig("interval");
            if (tmp != "") numericUpDown1.Value = int.Parse(tmp);

            //tmp = ReadConfig("currentId");
            //if (tmp != "") currentId = int.Parse(tmp);
        }



        void saveConfig()
        {
            WriteConfig("dbServer", textBox1.Text.Trim());
            WriteConfig("Uid", textBox2.Text.Trim());
            WriteConfig("Password", textBox3.Text.Trim());
            WriteConfig("dbName", textBox4.Text.Trim());

            WriteConfig("Client", textBox6.Text.Trim());
            WriteConfig("Server", textBox7.Text.Trim());
            WriteConfig("SendPort", numericUpDown2.Value.ToString());
            WriteConfig("RecvPort", numericUpDown3.Value.ToString());
            WriteConfig("begin", begin.ToString());
            WriteConfig("interval", numericUpDown1.Value.ToString());
            //WriteConfig("currentId", currentId.ToString());
        }

        private void threadLogMsg(DateTime dt1, string Msg)
        {
            this.Invoke(new Action(() =>
            {
                Log.Message(ref listView1, dt1, Msg);
            }));
        }


        private void threadLogMsg(DateTime dt1, string Msg, string tbName)
        {
            this.Invoke(new Action(() =>
            {
                Log.Message(ref listView1, dt1, Msg, tbName);
            }));
        }

        void loadXML()
        {
            XmlDocument xd = new XmlDocument();
            XmlReaderSettings settings = new XmlReaderSettings();
            settings.IgnoreComments = true;
            XmlReader reader = XmlReader.Create("TableList.xml", settings);
            xd.Load(reader);
            XmlNode xn = xd.SelectSingleNode("table");
            XmlNodeList xnl = xn.ChildNodes;
            reader.Close();

            foreach (XmlNode xmlNode in xnl)
            {
                switch (xmlNode.Name)
                {
                    case "name":
                        table.name = xmlNode.InnerText;
                        break;
                    case "time":
                        table.time = xmlNode.InnerText;
                        break;
                    case "uid":
                        table.UID = xmlNode.InnerText;
                        break;
                    case "id":
                        table.id = xmlNode.InnerText;
                        break;
                }
            }
        }

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            DialogResult result = MessageBox.Show("您确定要关闭数据发送服务程序？", "提示", MessageBoxButtons.OKCancel, MessageBoxIcon.Question);
            if (result == DialogResult.OK)
            {
                Application.Exit();//关闭程序
            }
            else
            {
                e.Cancel = true;//保持状态，这句必须有，防止有时候点了 “取消” 还是照样关闭
                //this.Hide(); //隐藏窗体 
                this.ShowInTaskbar = true;//图标不显示在任务栏上 
            }
        }

        private void listView1_MouseDoubleClick(object sender, MouseEventArgs e)
        {
            ListView listview = (ListView)sender;
            ListViewItem lstrow = listview.GetItemAt(e.X, e.Y);
            System.Windows.Forms.ListViewItem.ListViewSubItem lstcol = lstrow.GetSubItemAt(e.X, e.Y);
            string strText = lstcol.Text;
            try
            {
                Clipboard.SetDataObject(strText);
                notifyIcon1.Visible = true;
                string info = string.Format("内容【{0}】已经复制到剪贴板", strText);
                notifyIcon1.ShowBalloonTip(1500, "提示", info, ToolTipIcon.Info);
            }
            catch (System.Exception ex)
            {
                MessageBox.Show(ex.Message, "提示", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }
    }
}
